﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Linq.Expressions;

namespace LigerMVC.Common.Extensions
{
    public static class IQueryableExtension
    {
        public static readonly log4net.ILog log = log4net.LogManager.GetLogger(typeof(IQueryableExtension));

        #region 查询条件 
         
  
         /// <summary>
        /// 数据过滤
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="data"></param>
        /// <param name="descriptor"></param>
        /// <returns></returns>
        public static IQueryable<T> Query<T>(this IQueryable<T> source, IEnumerable<QueryCondition> Condition, bool isAlso = true)
            where T : class
        {
            var parser = new QueryExpressionParser<T>();
            var filter = parser.Parse(Condition, isAlso);
              
            var result = source.Where(filter);
            return result;
        }

        /// <summary>
        /// 数据过滤
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source"></param>
        /// <param name="Condition"></param>
        /// <returns></returns>
        public static IQueryable<T> Query<T>(this IQueryable<T> source, FilterGroup Condition)
            where T : class
        {
            var parser = new QueryExpressionParser<T>();
            var filter = parser.Parse(Condition);

            var result = source.Where(filter);
            return result;
        }

        #endregion

        #region 排序
        /// <summary>
        /// 排序
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source"></param>
        /// <param name="propertyName"></param>
        /// <param name="ascending"></param>
        /// <returns></returns>
        public static IQueryable<T> Query<T>(this IQueryable<T> source, string propertyName, bool ascending = true) where T : class
        {
            Type type = typeof(T);

            PropertyInfo property = type.GetProperty(propertyName);
            if (property == null)
                throw new ArgumentException("propertyName", "Not Exist");

            ParameterExpression param = Expression.Parameter(type, "p");
            Expression propertyAccessExpression = Expression.MakeMemberAccess(param, property);
            LambdaExpression orderByExpression = Expression.Lambda(propertyAccessExpression, param);

            string methodName = ascending ? "OrderBy" : "OrderByDescending";

            MethodCallExpression resultExp = Expression.Call(typeof(Queryable), methodName, new Type[] { type, property.PropertyType }, source.Expression, Expression.Quote(orderByExpression));

            return source.Provider.CreateQuery<T>(resultExp);
        }
        /// <summary>
        /// 排序
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source"></param>
        /// <param name="OrderBy"></param>
        /// <returns></returns>
        public static IQueryable<T> Query<T>(this IQueryable<T> source, OrderByClause OrderBy)
           where T : class
        { 
            Type type = typeof(T);
            var parameter = Expression.Parameter(type); 
            var propertyInfo = type.GetProperty(OrderBy.Key);
            Expression propertySelector = Expression.Property(parameter, propertyInfo); 
            //To deal with different type of the property selector, very hacky
            //looking for a better solution
            if (propertyInfo.PropertyType == typeof(string))
            {
                var orderby = Expression.Lambda<Func<T, string>>(propertySelector, parameter);
                if (OrderBy.Order == OrderSequence.DESC)
                    source = source.OrderByDescending(orderby);
                else
                    source = source.OrderBy(orderby);
            }
            else if (propertyInfo.PropertyType == typeof(decimal))
            {
                var orderby = Expression.Lambda<Func<T, decimal>>(propertySelector, parameter);
                if (OrderBy.Order == OrderSequence.DESC)
                    source = source.OrderByDescending(orderby);
                else
                    source = source.OrderBy(orderby);
            }
            else if (propertyInfo.PropertyType == typeof(int))
            {
                var orderby = Expression.Lambda<Func<T, int>>(propertySelector, parameter);
                if (OrderBy.Order == OrderSequence.DESC)
                    source = source.OrderByDescending(orderby);
                else
                    source = source.OrderBy(orderby);
            }
            else if (propertyInfo.PropertyType == typeof(double))
            {
                var orderby = Expression.Lambda<Func<T, double>>(propertySelector, parameter);
                if (OrderBy.Order == OrderSequence.DESC)
                    source = source.OrderByDescending(orderby);
                else
                    source = source.OrderBy(orderby);
            }
            else if (propertyInfo.PropertyType == typeof(float))
            {
                var orderby = Expression.Lambda<Func<T, float>>(propertySelector, parameter);
                if (OrderBy.Order == OrderSequence.DESC)
                    source = source.OrderByDescending(orderby);
                else
                    source = source.OrderBy(orderby);
            } 
            return source;
        }

        #endregion

        #region 数据检索(分页 查询 排序)
        /// <summary>
        /// 数据检索
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="data"></param>
        /// <param name="descriptor"></param>
        /// <returns></returns>
        public static IQueryable<T> Query<T>(this IQueryable<T> source, QuerySimpleDescriptor descriptor)
            where T : class
        {
            //查询
            var result = source.Query(descriptor.Condition, descriptor.IsAlso);

            //排序
            if (descriptor.OrderBy != null)
            {
                result = result.Query(descriptor.OrderBy.Key, descriptor.OrderBy.Order == OrderSequence.ASC);
            } 
            //paging
            if (descriptor.PageSize > 0)
            {  
                return result.Skip((descriptor.PageIndex - 1) * descriptor.PageSize).Take(descriptor.PageSize);
            }
            return result; 
        }

        /// <summary>
        /// 数据检索
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source"></param>
        /// <param name="descriptor"></param>
        /// <returns></returns>
        public static IQueryable<T> Query<T>(this IQueryable<T> source, QueryDescriptor descriptor)
           where T : class
        {
            //查询 
            var parser = new QueryExpressionParser<T>();
            var where = parser.Parse(descriptor.Condition);
            var result = source.Where(where);

            //排序
            if (descriptor.OrderBy != null && descriptor.OrderBy.Any())
            {
                result = result.Query(descriptor.OrderBy[0].Key, descriptor.OrderBy[0].Order == OrderSequence.ASC);
            }
            //paging
            if (descriptor.PageSize > 0)
            {
                return result.Skip((descriptor.PageIndex - 1) * descriptor.PageSize).Take(descriptor.PageSize);
            }
            return result;
        }


        /// <summary>
        /// 数据检索,并输出 分页前 总记录数
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source"></param>
        /// <param name="descriptor"></param>
        /// <param name="totalPages"></param>
        /// <returns></returns>
        public static IQueryable<T> Query<T>(this IQueryable<T> source, QuerySimpleDescriptor descriptor, out int total)
    where T : class
        {
            total = 0;
            //查询
            var result = source.Query(descriptor.Condition, descriptor.IsAlso);

            //排序
            if (descriptor.OrderBy != null)
            {
                result = result.Query(descriptor.OrderBy.Key, descriptor.OrderBy.Order == OrderSequence.ASC);
            }
            total = result.Count();
            //paging
            if (descriptor.PageSize > 0)
            { 
                return result.Skip((descriptor.PageIndex - 1) * descriptor.PageSize).Take(descriptor.PageSize);
            } 
            return result;
        }

         /// <summary>
        /// 数据检索,并输出 分页前 总记录数
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source"></param>
        /// <param name="descriptor"></param>
        /// <param name="totalPages"></param>
        /// <returns></returns>
        public static IQueryable<T> Query<T>(this IQueryable<T> source, QueryDescriptor descriptor, out int total)
    where T : class
        {
            total = 0;
            //查询
            var parser = new QueryExpressionParser<T>();
            var where = parser.Parse(descriptor.Condition);
            var result = source.Where(where);

            //排序
            if (descriptor.OrderBy != null && descriptor.OrderBy.Any() )
            {
                result = result.Query(descriptor.OrderBy[0].Key, descriptor.OrderBy[0].Order == OrderSequence.ASC);
            }
            total = result.Count();
            //paging
            if (descriptor.PageSize > 0)
            {
                return result.Skip((descriptor.PageIndex - 1) * descriptor.PageSize).Take(descriptor.PageSize);
            }
            return result;
        }
       
        /// <summary>
        /// 获取分页数据
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source"></param>
        /// <param name="descriptor"></param>
        /// <returns></returns>
        public static PagedData<T> Page<T>(this IQueryable<T> source, QuerySimpleDescriptor descriptor)
   where T : class
        {
            int total ;
            var result = source.Query(descriptor, out total);

            return new PagedData<T>()
            {
                Rows = result.ToList(),
                Total = total
            };
        }

        /// <summary>
        /// 获取分页数据
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source"></param>
        /// <param name="descriptor"></param>
        /// <returns></returns>
        public static PagedData<T> Page<T>(this IQueryable<T> source, QueryDescriptor descriptor)
   where T : class
        {
            int total;
            var result = source.Query(descriptor, out total);
            log.Info(result.ToString());
            return new PagedData<T>()
            {
                Rows = result.ToList(),
                Total = total
            };
        }
        #endregion

    }
}
